package nl.zuiderfestival.raydance.control;

import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.InetAddress;
import java.net.UnknownHostException;

import javax.swing.JFormattedTextField;
import javax.swing.JOptionPane;
import javax.swing.event.DocumentEvent;

import nl.zuiderfestival.raydance.model.RayDance;
import nl.zuiderfestival.raydance.model.zones.Zone;
import nl.zuiderfestival.raydance.view.ConnectionPanel;
import nl.zuiderfestival.raydance.view.RayDanceFrame;
import nl.zuiderfestival.raydance.view.ZonePanel;
import nl.zuiderfestival.raydance.view.canvas.ZoneDisplay;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RayDanceController extends WindowAdapter implements RayDanceListener {

	private static final Logger log = LoggerFactory.getLogger(RayDanceController.class);

	private RayDance model;
	private RayDanceFrame frame;

	public RayDanceController(RayDance model, RayDanceFrame frame) {
		this.model = model;
		this.frame = frame;

		model.addObserver(frame);
		model.addObserver(frame.getZonePanel());
		model.addObserver(frame.getZoneDisplay());

		model.getArduinoManager().addObserver(frame.getConnectionPanel());
		model.getRadarManager().addObserver(frame.getConnectionPanel());
		model.getRadarManager().addTuioListener(frame.getTuioParticleDisplay());

		frame.addRayDanceListener(this);
		frame.addWindowListener(this);
		log.debug("Created");
	}

	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();
		log.debug("Received ActionEvent '"+command+"'");
		if (command.equals(ZonePanel.CHANGE_SELECTION)) {
			changeSelection();
		} else if (command.equals(ZonePanel.ADD)) {
			addZone();
		} else if (command.equals(ZonePanel.REMOVE)) {
			removeZone();
		} else if (command.equals(ConnectionPanel.EDITARDUINO)) {
			editArduino();
		} else if (command.equals(ConnectionPanel.EDITRADAR)) {
			editRadar();
		} else if (command.equals(ZoneDisplay.SHAPE_SELECTION)) {
			changedShapeSelection();
		} else if (command.equals(ConnectionPanel.TOGGLE_MOVEMENT_DISPLAY)) {
			frame.toggleMovementDisplay();
		}
	}

	public void insertUpdate(DocumentEvent event) {
		JFormattedTextField field = (JFormattedTextField) event.getDocument().getProperty("owner");
		String command = (String) event.getDocument().getProperty("command");
		log.debug("Received DocumentEvent '"+command+"'"); 
		if (command.equals(ZonePanel.CHANGE_HEIGHT)) {
			changeHeight(field);
			frame.getZoneDisplay().repaint();
		} else if (command.equals(ZonePanel.CHANGE_WIDTH)) {
			changeWidth(field);
			frame.getZoneDisplay().repaint();
		} else if (command.equals(ZonePanel.CHANGE_X)) {
			changeY(field);
			frame.getZoneDisplay().repaint();
		} else if (command.equals(ZonePanel.CHANGE_Y)) {
			changeX(field);
			frame.getZoneDisplay().repaint();
		}
	}

	public void removeUpdate(DocumentEvent event) {
		//Do nothing
	}

	public void changedUpdate(DocumentEvent event) {
		//Do nothing
	}

	private void changeX(JFormattedTextField source) {
		if (source.getValue() == null)
			return;

		int value = ((Integer)source.getValue()).intValue();
		Zone zone = model.getSelectedZone();
		if (zone != null) {
			zone.getLocation().x = value;
		}
	}

	private void changeY(JFormattedTextField source) {
		if (source.getValue() == null)
			return;

		int value = ((Integer)source.getValue()).intValue();
		Zone zone = model.getSelectedZone();
		if (zone != null) {
			zone.getLocation().y = value;
		}
	}

	private void changeWidth(JFormattedTextField source) {
		if (source.getValue() == null)
			return;

		int value = ((Integer)source.getValue()).intValue();
		Zone zone = model.getSelectedZone();
		if (zone != null) {
			zone.getSize().width = value;
		}
	}

	private void changeHeight(JFormattedTextField source) {
		if (source.getValue() == null)
			return;

		int value = ((Integer)source.getValue()).intValue();
		Zone zone = model.getSelectedZone();
		if (zone != null) {
			zone.getSize().height = value;
		}
	}

	private void addZone() {
		String s = (String)JOptionPane.showInputDialog(
				frame,
				"Zone id [0-64]:",
				"Add new zone",
				JOptionPane.PLAIN_MESSAGE);
		if (s != null) {
			try {
				int id = Integer.parseInt(s);
				if (id >= 0 && id <= 64) {
					Zone zone = model.createNewZone(id);
					model.setSelectedZone(zone);
					return;
				} 
			} catch (NumberFormatException e) {
				//do nothing
			}

			JOptionPane.showMessageDialog(frame,
					"Illegal id number '"+s+"', zone not added",
					"Inane error",
					JOptionPane.ERROR_MESSAGE);
		}
	}

	private void removeZone() {
		Zone selectedZone = frame.getSelectedZone();
		if (selectedZone != null && selectedZone.equals(model.getSelectedZone())) {
			model.setSelectedZone(null);
			model.removeZone(selectedZone);
		}
	}

	private void changeSelection() {
		Zone selectedZone = frame.getSelectedZone();
		if (selectedZone != null && !selectedZone.equals(model.getSelectedZone())) {
			model.setSelectedZone(selectedZone);
		}
	}

	private void changedShapeSelection() {
		Zone selectedZone = frame.getSelectedShapeZone();
		if (selectedZone != null && !selectedZone.equals(model.getSelectedZone())) {
			model.setSelectedZone(selectedZone);
		}
	}

	private void editRadar() {
		String s = (String)JOptionPane.showInputDialog(
				frame,
				"Enter port number [255-9999], defaults to 3333",
				"Radaratouch port dialog",
				JOptionPane.PLAIN_MESSAGE);


		if ((s != null) && (s.length() > 0)) {
			try {
				int port = Integer.parseInt(s);
				if (port >= 255 && port <= 9999) {
					model.changeRadarPortTo(port);
					model.getRadarManager().addTuioListener(frame.getTuioParticleDisplay());
					return;
				} 
			} catch (NumberFormatException e) {
				//do nothing
			}

			JOptionPane.showMessageDialog(frame,
					"Illegal port number '"+s+"', made no changes",
					"Inane error",
					JOptionPane.ERROR_MESSAGE);
		}

	}

	private void editArduino() {
		String s = (String)JOptionPane.showInputDialog(
				frame,
				"Enter {hostname:port} defaults to localhost:9876, instead of host, u can enter an ip address aswell.",
				"Arduino host:port dialog",
				JOptionPane.PLAIN_MESSAGE);

		boolean invalidPort = false;
		boolean invalidHost = false;
		
		if ((s != null) && (s.length() > 0)) {
			String[] splitted = s.split(":");
			if (splitted.length==2) {
				try {
					
					InetAddress.getByName(splitted[0]);
					
					int port = Integer.parseInt(splitted[1]);
					if (port >= 255 && port <= 9999) {
						model.updateArduinoConnection(splitted[0], port);
						return;
					}  else {
						invalidPort = true;
					}
				} catch (NumberFormatException e) {
					invalidPort = true;
				} catch (UnknownHostException e) {
					invalidHost = true;
				}
			} 

			
			String type = invalidHost ? "host" : (invalidPort ? "port number" : "value");
			String value = type.equals("host") ? splitted[0] : splitted[1];
			JOptionPane.showMessageDialog(frame,
					"Illegal "+type+" '"+value+"', made no changes",
					"Inane error",
					JOptionPane.ERROR_MESSAGE);
		}
	}

	public void windowClosing(WindowEvent e) {
		model.save();
		System.exit(0);
	}

}
